package com.kunpeng.weibo.sina.http;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.zip.GZIPInputStream;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.json.JSONObject;

import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.text.TextUtils;

import com.kunpeng.weibo.sina.SinaWeibo;
import com.kunpeng.weibo.sina.beans.Oauth2AccessToken;
import com.kunpeng.weibo.sina.beans.WeiboParameters;

public class HttpTask {
	public static final String	BOUNDARY				= "7cd4a6d158c";
	public static final String	MP_BOUNDARY				= "--" + BOUNDARY;
	public static final String	END_MP_BOUNDARY			= "--" + BOUNDARY + "--";
	public static final String	MULTIPART_FORM_DATA		= "multipart/form-data";

	public static final String	HTTPMETHOD_POST			= "POST";
	public static final String	HTTPMETHOD_GET			= "GET";
	public static final String	HTTPMETHOD_DELETE		= "DELETE";

	private static final int	SET_CONNECTION_TIMEOUT	= 50000;
	private static final int	SET_SOCKET_TIMEOUT		= 200000;

	public static final int		REQUEST_SUCCESS			= 200;						//请求成功

	/**
	 * Implement a weibo http request and return results .
	 * 
	 * @param context : context of activity
	 * @param url : request url of open api
	 * @param method : HTTP METHOD.GET, POST, DELETE
	 * @param params : Http params , query or postparameters
	 * @param Token : oauth token or accesstoken
	 * @return UrlEncodedFormEntity: encoed entity
	 * @throws SinaException
	 */

	public int openUrl(String url, String method, WeiboParameters params, Oauth2AccessToken token) {
		int resultCode = -1;
		String file = "";
		for (int loc = 0; loc < params.size(); loc++) {
			String key = params.getKey(loc);
			if (key.equals("pic")) {
				file = params.getValue(key);
				params.remove(key);
			}
		}
		if (TextUtils.isEmpty(file)) {
			resultCode = openUrl(url, method, params, null, token);
		} else {
			resultCode = openUrl(url, method, params, file, token);
		}
		return resultCode;
	}

	public int openUrl(String url, String method, WeiboParameters params, String file, Oauth2AccessToken token) {
		int resultCode = -1;
		HttpUriRequest request = null;
		ByteArrayOutputStream bos = null;
		try {
			HttpClient client = getNewHttpClient();
			if (method.equals("GET")) {
				url = url + "?" + encodeUrl(params);
				HttpGet get = new HttpGet(url);
				request = get;
			} else if (method.equals("POST")) {
				HttpPost post = new HttpPost(url);
				byte[] data = null;
				bos = new ByteArrayOutputStream(1024 * 50);
				if (!TextUtils.isEmpty(file)) {
					paramToUpload(bos, params);
					post.setHeader("Content-Type", MULTIPART_FORM_DATA + "; boundary=" + BOUNDARY);
					Bitmap bf = BitmapFactory.decodeFile(file);
					imageContentToUpload(bos, bf);
				} else {
					post.setHeader("Content-Type", "application/x-www-form-urlencoded");
					String postParam = encodeParameters(params);
					data = postParam.getBytes("UTF-8");
					bos.write(data);
				}
				data = bos.toByteArray();
				bos.close();
				// UrlEncodedFormEntity entity = getPostParamters(params);
				ByteArrayEntity formEntity = new ByteArrayEntity(data);
				post.setEntity(formEntity);
				request = post;
			} else if (method.equals("DELETE")) {
				request = new HttpDelete(url);
			}
			setHeader(method, request, params, url, token);
			HttpResponse response = client.execute(request);
			StatusLine status = response.getStatusLine();
			int statusCode = status.getStatusCode();

			if (statusCode != REQUEST_SUCCESS) {
				String result = read(response);
				JSONObject json = new JSONObject(result);
				resultCode = json.getInt("error_code");
			} else {
				resultCode = REQUEST_SUCCESS;
			}
			// parse content stream from response
			//			result = read(response);
			return resultCode;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (bos != null) {
				try {
					bos.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		return resultCode;
	}

	public HttpClient getNewHttpClient() throws Exception {
		KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
		trustStore.load(null, null);

		SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
		sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

		HttpParams params = new BasicHttpParams();

		//		HttpConnectionParams.setConnectionTimeout(params, 10000);
		//		HttpConnectionParams.setSoTimeout(params, 10000);

		HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
		HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

		SchemeRegistry registry = new SchemeRegistry();
		registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
		registry.register(new Scheme("https", sf, 443));

		ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

		// Set the default socket timeout (SO_TIMEOUT) // in
		// milliseconds which is the timeout for waiting for data.
		HttpConnectionParams.setConnectionTimeout(params, SET_CONNECTION_TIMEOUT);
		HttpConnectionParams.setSoTimeout(params, SET_SOCKET_TIMEOUT);
		HttpClient client = new DefaultHttpClient(ccm, params);
		return client;
	}

	public String encodeUrl(WeiboParameters parameters) {
		if (parameters == null) {
			return "";
		}

		StringBuilder sb = new StringBuilder();
		boolean first = true;
		for (int loc = 0; loc < parameters.size(); loc++) {
			if (first)
				first = false;
			else
				sb.append("&");
			sb.append(URLEncoder.encode(parameters.getKey(loc)) + "=" + URLEncoder.encode(parameters.getValue(loc)));
		}
		return sb.toString();
	}

	/**
	 * Upload weibo contents into output stream .
	 * 
	 * @param baos : output stream for uploading weibo
	 * @param params : post parameters for uploading
	 * @return void
	 * @throws IOException
	 */
	private void paramToUpload(OutputStream baos, WeiboParameters params) throws IOException {
		String key = "";
		for (int loc = 0; loc < params.size(); loc++) {
			key = params.getKey(loc);
			StringBuilder temp = new StringBuilder(10);
			temp.setLength(0);
			temp.append(MP_BOUNDARY).append("\r\n");
			temp.append("content-disposition: form-data; name=\"").append(key).append("\"\r\n\r\n");
			temp.append(params.getValue(key)).append("\r\n");
			byte[] res = temp.toString().getBytes();
			baos.write(res);
		}
	}

	/**
	 * Upload image into output stream .
	 * 
	 * @param out : output stream for uploading weibo
	 * @param imgpath : bitmap for uploading
	 * @return void
	 * @throws IOException
	 */
	private void imageContentToUpload(OutputStream out, Bitmap imgpath) throws IOException {
		StringBuilder temp = new StringBuilder();

		temp.append(MP_BOUNDARY).append("\r\n");
		temp.append("Content-Disposition: form-data; name=\"pic\"; filename=\"").append("news_image").append("\"\r\n");
		String filetype = "image/png";
		temp.append("Content-Type: ").append(filetype).append("\r\n\r\n");
		byte[] res = temp.toString().getBytes();
		out.write(res);
		imgpath.compress(CompressFormat.PNG, 75, out);
		out.write("\r\n".getBytes());
		out.write(("\r\n" + END_MP_BOUNDARY).getBytes());
	}

	public String encodeParameters(WeiboParameters httpParams) throws UnsupportedEncodingException {
		if (null == httpParams || isBundleEmpty(httpParams)) {
			return "";
		}
		StringBuilder buf = new StringBuilder();
		int j = 0;
		for (int loc = 0; loc < httpParams.size(); loc++) {
			String key = httpParams.getKey(loc);
			if (j != 0) {
				buf.append("&");
			}
			buf.append(URLEncoder.encode(key, "UTF-8")).append("=").append(URLEncoder.encode(httpParams.getValue(key).replace("+", "%2B"), "UTF-8"));
			j++;
		}
		return buf.toString();

	}

	// 设置http头,如果authParam不为空，则表示当前有token认证信息需要加入到头中
	public void setHeader(String httpMethod, HttpUriRequest request, WeiboParameters authParam, String url, Oauth2AccessToken token) throws UnsupportedEncodingException {
		if (!isBundleEmpty(authParam)) {
			Oauth2AccessTokenHeader header = new Oauth2AccessTokenHeader();
			String authHeader = header.getWeiboAuthHeader(httpMethod, url, authParam, SinaWeibo.SINA_APP_KEY, SinaWeibo.SINA_APP_SECRET, token);
			if (authHeader != null) {
				request.setHeader("Authorization", authHeader);
			}
		}
		request.setHeader("User-Agent", System.getProperties().getProperty("http.agent") + " WeiboAndroidSDK");
	}

	public boolean isBundleEmpty(WeiboParameters bundle) {
		if (bundle == null || bundle.size() == 0) {
			return true;
		}
		return false;
	}

	/**
	 * Read http requests result from response .
	 * 
	 * @param response : http response by executing httpclient
	 * 
	 * @return String : http response content
	 * @throws IOException
	 * @throws IllegalStateException
	 */
	private String read(HttpResponse response) throws IllegalStateException, IOException {
		String result = "";
		HttpEntity entity = response.getEntity();
		InputStream inputStream = entity.getContent();
		ByteArrayOutputStream content = new ByteArrayOutputStream();

		Header header = response.getFirstHeader("Content-Encoding");
		if (header != null && header.getValue().toLowerCase().indexOf("gzip") > -1) {
			inputStream = new GZIPInputStream(inputStream);
		}

		// Read response into a buffered stream
		int readBytes = 0;
		byte[] sBuffer = new byte[512];
		while ((readBytes = inputStream.read(sBuffer)) != -1) {
			content.write(sBuffer, 0, readBytes);
		}
		// Return result from buffered stream
		result = new String(content.toByteArray());
		return result;
	}

	public static class MySSLSocketFactory extends SSLSocketFactory {
		SSLContext	sslContext	= SSLContext.getInstance("TLS");

		public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
			super(truststore);

			TrustManager tm = new X509TrustManager() {
				public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}

				public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}

				public X509Certificate[] getAcceptedIssuers() {
					return null;
				}
			};

			sslContext.init(null, new TrustManager[] { tm }, null);
		}

		@Override
		public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
			return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
		}

		@Override
		public Socket createSocket() throws IOException {
			return sslContext.getSocketFactory().createSocket();
		}
	}
}
